package com.example.netty.handler;

import com.example.netty.bean.DeviceMsg;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;

import java.nio.charset.Charset;
import java.util.List;

/**
 * @Author : lichong
 * @description :
 * @Date Create in 11:18 2020/1/12
 * @Modified By :
 **/
public class MessageDecoder extends ByteToMessageDecoder {
    private static final Charset charset = Charset.forName("utf-8");
    private final ByteBuf prefixBuf = Unpooled.copiedBuffer(DeviceMsg.MSG_START.getBytes());
    private final ByteBuf suffixBuf = Unpooled.copiedBuffer(DeviceMsg.MSG_END.getBytes());

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) {
        //判断数据是否到最小消息体
        ByteBuf messageBodyBuf = getMessageBodyBuf(buffer);
        if (messageBodyBuf != null) {
            out.add(messageBodyBuf.toString(charset));
        }
    }

    /**
     * 处理粘包，丢包问题获取包体信息
     * @param byteBuf
     * @return
     */
    public ByteBuf getMessageBodyBuf(ByteBuf byteBuf) {
        //获取包体长度
        int capacity = byteBuf.readableBytes();
        //获取第一个前缀的索引
        int preIndex = index(byteBuf, prefixBuf);
        //获取第二个前缀的索引
        int secPreIndex = index(byteBuf, prefixBuf, preIndex + prefixBuf.capacity());
        //获取后缀的索引
        int sufIndex = index(byteBuf, suffixBuf);
        //如果第二个开始索引大于第一个开始索引，并且小于结束索引，则第一个开始索引数据舍弃
        //或者如果存在第一个第二个人开始标识但是没有结尾标识，则跳过第一个开始标识
        if (secPreIndex > preIndex && secPreIndex < sufIndex
                || secPreIndex > preIndex && sufIndex < 0) {
            preIndex = secPreIndex;
        }
        //如果既有开始标识，并且结束标识大于开始标识的索引，说明是正常的数据包
        if (preIndex > -1 && sufIndex > preIndex) {
            //忽略开头之前的包体
            byteBuf.skipBytes(preIndex);
            return byteBuf.readRetainedSlice(sufIndex - preIndex + 1);
        } else if (preIndex > -1) {
            //只有开头没有结尾，舍弃开头标识之前的数据
            byteBuf.skipBytes(preIndex);
        } else {
            //没有开头，舍弃整个包体
            byteBuf.skipBytes(capacity);
        }
        return null;
    }

    /**
     * 从指定的开始位置到写的索引位置之间获取byte的位置索引，
     * @param haystack  被检索的byte
     * @param needle
     * @param fromIndex 如果fromIndex==0则默认是从读的位置开始
     * @return
     */
    private int index(ByteBuf haystack, ByteBuf needle, int fromIndex) {
        int readerIndex = haystack.readerIndex();
        int writerIndex = haystack.writerIndex();
        //如果开始的索引是0则默认为从读的索引开始
        fromIndex = fromIndex == 0 ? readerIndex : fromIndex;
        //如果开始索引大于写的索引返回-1
        if (fromIndex > writerIndex) {
            return -1;
        }
        //如果开始索引小于已读的索引，则
        if (fromIndex < readerIndex) {
            fromIndex = readerIndex + fromIndex;
        }
        for (int i = fromIndex; i < writerIndex; i ++) {
            int haystackIndex = i;
            int needleIndex;
            for (needleIndex = 0; needleIndex < needle.capacity(); needleIndex ++) {
                if (haystack.getByte(haystackIndex) != needle.getByte(needleIndex)) {
                    break;
                } else {
                    haystackIndex ++;
                    if (haystackIndex == haystack.writerIndex() &&
                            needleIndex != needle.capacity() - 1) {
                        return -1;
                    }
                }
            }

            if (needleIndex == needle.capacity()) {
                // Found the needle from the haystack!
                return i - haystack.readerIndex();
            }
        }
        return -1;
    }

    /**
     * Returns the number of bytes between the readerIndex of the haystack and
     * the first needle found in the haystack.  -1 is returned if no needle is
     * found in the haystack.
     */
    private int index(ByteBuf haystack, ByteBuf needle) {
        return index(haystack, needle, 0);
    }
}
